package coupon

import (
	"context"
	"fmt"

	"mall/common/globalkey"
	"mall/common/resultx"
	enum "mall/service/backend/common/enum/promotion"
	"mall/service/promotion/model"
	"mall/service/promotion/rpc/internal/svc"
	"mall/service/promotion/rpc/types/promotion"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type CheckCouponScopeLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewCheckCouponScopeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CheckCouponScopeLogic {
	return &CheckCouponScopeLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *CheckCouponScopeLogic) CheckCouponScope(in *promotion.CheckCouponScopeReq) (*promotion.CouponRelationshipInfo, error) {
	logrus.Info(fmt.Sprintf("CheckCouponScope: %+v", in))

	if in.CouponId == 0 || len(in.Skus) == 0 {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	data, err := l.svcCtx.CouponTemplate.FindOne(l.ctx, in.CouponId)
	if err != nil {
		if err == model.ErrNotFound {
			return nil, resultx.StatusError(resultx.COUPON_ID_INVALID, "")
		}

		return nil, resultx.StatusError(resultx.DB_ERROR, "")
	}

	var resp *promotion.CouponRelationshipInfo
	switch data.ScopeType {
	case enum.PromotionScopeAll:
		return &promotion.CouponRelationshipInfo{}, nil
	case enum.PromotionScopeCategory:
		var categoryIds []any
		cidMap := make(map[uint64]struct{})
		categoryIdSkuIdMap := make(map[uint64][]uint64)
		for _, v := range in.Skus {
			if v.VariationId != nil && *v.VariationId > 0 && len(v.CategoryIds) > 0 {
				for _, cid := range v.CategoryIds {
					categoryIdSkuIdMap[cid] = append(categoryIdSkuIdMap[cid], *v.VariationId)
					if _, ok := cidMap[cid]; !ok {
						categoryIds = append(categoryIds, cid)
						cidMap[cid] = struct{}{}
					}
				}
			}
		}
		resp, err = l.checkCouponProductCategoryScope(in.CouponId, categoryIds, categoryIdSkuIdMap)
	case enum.PromotionScopeBrand:
		var brandIds []any
		bidMap := make(map[uint64]struct{})
		brandIdSkuIdMap := make(map[uint64][]uint64)
		for _, v := range in.Skus {
			if v.VariationId != nil && *v.VariationId > 0 && v.BrandId != nil && *v.BrandId > 0 {
				brandIdSkuIdMap[*v.BrandId] = append(brandIdSkuIdMap[*v.BrandId], *v.VariationId)
				if _, ok := bidMap[*v.BrandId]; !ok {
					brandIds = append(brandIds, *v.BrandId)
					bidMap[*v.BrandId] = struct{}{}
				}
			}
		}
		resp, err = l.checkCouponProductBrandScope(in.CouponId, brandIds, brandIdSkuIdMap)
	case enum.PromotionScopeProduct:
		var skuIds []any
		skuIdMap := make(map[uint64]struct{})
		for _, v := range in.Skus {
			if v.VariationId != nil && *v.VariationId > 0 {
				if _, ok := skuIdMap[*v.VariationId]; !ok {
					skuIds = append(skuIds, *v.VariationId)
					skuIdMap[*v.VariationId] = struct{}{}
				}
			}
		}
		resp, err = l.checkCouponProductScope(in.CouponId, skuIds)
	}
	if err != nil {
		return nil, resultx.StatusError(resultx.DB_ERROR, "")
	}

	return resp, nil
}

func (l *CheckCouponScopeLogic) checkCouponProductScope(
	couponId uint64,
	skuIds []any,
) (*promotion.CouponRelationshipInfo, error) {
	if len(skuIds) == 0 {
		return &promotion.CouponRelationshipInfo{
			TemplateId: couponId,
		}, nil
	}

	filterMap := make(map[string]map[string][]any)
	filterMap[model.PromotionCouponProductRelationshipColumns.CouponID] = map[string][]any{
		"equalTo": []any{couponId},
	}
	filterMap[model.PromotionCouponProductRelationshipColumns.Status] = map[string][]any{
		"equalTo": []any{globalkey.StatusEnabled},
	}
	filterMap[model.PromotionCouponProductRelationshipColumns.SkuID] = map[string][]any{
		"in": skuIds,
	}
	res, _, err := l.svcCtx.CouponTemplate.GetProductList(l.ctx, model.M{}, -1, filterMap)
	if err != nil {
		return nil, err
	}

	resp := &promotion.CouponRelationshipInfo{
		TemplateId: couponId,
	}
	for _, v := range res {
		resp.SkuIds = append(resp.SkuIds, v.SkuID)
	}

	return resp, nil
}

func (l *CheckCouponScopeLogic) checkCouponProductBrandScope(
	couponId uint64,
	brandIds []any,
	brandIdSkuIdMap map[uint64][]uint64,
) (*promotion.CouponRelationshipInfo, error) {
	if len(brandIds) == 0 {
		return &promotion.CouponRelationshipInfo{
			TemplateId: couponId,
		}, nil
	}

	filterMap := make(map[string]map[string][]any)
	filterMap[model.PromotionCouponProductBrandRelationshipColumns.CouponID] = map[string][]any{
		"equalTo": []any{couponId},
	}
	filterMap[model.PromotionCouponProductBrandRelationshipColumns.Status] = map[string][]any{
		"equalTo": []any{globalkey.StatusEnabled},
	}
	filterMap[model.PromotionCouponProductBrandRelationshipColumns.ProductBrandID] = map[string][]any{
		"in": brandIds,
	}
	res, _, err := l.svcCtx.CouponTemplate.GetProductBrandList(l.ctx, model.M{}, -1, filterMap)
	if err != nil {
		return nil, err
	}

	resp := &promotion.CouponRelationshipInfo{
		TemplateId: couponId,
	}
	for _, v := range res {
		if skuIds, ok := brandIdSkuIdMap[v.ProductBrandID]; ok {
			resp.SkuIds = append(resp.SkuIds, skuIds...)
		}
	}

	return resp, nil
}

func (l *CheckCouponScopeLogic) checkCouponProductCategoryScope(
	couponId uint64,
	categoryIds []any,
	categoryIdSkuIdMap map[uint64][]uint64,
) (*promotion.CouponRelationshipInfo, error) {
	if len(categoryIds) == 0 {
		return &promotion.CouponRelationshipInfo{
			TemplateId: couponId,
		}, nil
	}

	filterMap := make(map[string]map[string][]any)
	filterMap[model.PromotionCouponProductCategoryRelationshipColumns.CouponID] = map[string][]any{
		"equalTo": []any{couponId},
	}
	filterMap[model.PromotionCouponProductCategoryRelationshipColumns.Status] = map[string][]any{
		"equalTo": []any{globalkey.StatusEnabled},
	}
	filterMap[model.PromotionCouponProductCategoryRelationshipColumns.ProductCategoryID] = map[string][]any{
		"in": categoryIds,
	}
	res, _, err := l.svcCtx.CouponTemplate.GetProductCategoryList(l.ctx, model.M{}, -1, filterMap)
	if err != nil {
		return nil, err
	}

	resp := &promotion.CouponRelationshipInfo{
		TemplateId: couponId,
	}
	for _, v := range res {
		if skuIds, ok := categoryIdSkuIdMap[v.ProductCategoryID]; ok {
			resp.SkuIds = append(resp.SkuIds, skuIds...)
		}
	}

	return resp, nil
}
